package com.wang.tracer;

import com.wang.model.ApiInfo;
import com.wang.model.CallChain;
import com.wang.model.MethodInfo;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * 调用链追踪器：从目标方法反向追踪到Controller接口的完整调用链
 *
 * @author 13741
 */
public class CallChainTracer {
    // 所有方法集合
    private final Map<String, MethodInfo> allMethods;
    // 方法调用关系图
    private final Map<MethodInfo, Set<MethodInfo>> methodCallGraph;
    // Controller接口映射
    private final Map<MethodInfo, ApiInfo> controllerApis;
    // 目标方法名（如 RedisUtil.set）
    private final String targetMethodName;
    // 目标类名（如 RedisUtil）
    private final String targetClassName;
    // 最大调用链深度（防止循环）
    private final int maxChainDepth;
    // 分析结果
    private final Map<CallChain, Set<ApiInfo>> impactResults = new LinkedHashMap<>();

    public CallChainTracer(Map<String, MethodInfo> allMethods, Map<MethodInfo, Set<MethodInfo>> methodCallGraph, Map<MethodInfo, ApiInfo> controllerApis, String targetMethodName, String targetClassName, int maxChainDepth) {
        this.allMethods = allMethods;
        this.methodCallGraph = methodCallGraph;
        this.controllerApis = controllerApis;
        this.targetMethodName = targetMethodName;
        this.targetClassName = targetClassName;
        this.maxChainDepth = maxChainDepth;
    }

    /**
     * 开始追踪调用链
     */
    public void trace() {
        List<MethodInfo> targetMethods = findTargetMethods();
        System.out.println("找到目标方法数量: " + targetMethods.size());

        for (MethodInfo targetMethod : targetMethods) {
            System.out.println("追踪目标方法: " + targetMethod.getFullName());
            traceFromTarget(targetMethod, new ArrayList<>(), 0);
        }
    }

    /**
     * 从目标方法开始递归反向追踪
     */
    private void traceFromTarget(MethodInfo targetMethod, List<MethodInfo> currentChain, int depth) {
        if (depth > maxChainDepth) {
            return;
        }
        // 检查当前方法是否是Controller方法
        if (controllerApis.containsKey(targetMethod)) {
            CallChain chain = new CallChain(new ArrayList<>(currentChain));
            ApiInfo apiInfo = controllerApis.get(targetMethod);

            Set<ApiInfo> apiSet = impactResults.computeIfAbsent(chain, k -> new HashSet<>());
            apiSet.add(apiInfo);
            System.out.println("找到Controller方法调用链: " + chain.toString());
            return;
        }

        // 查找调用当前方法的所有方法
        Set<MethodInfo> callers = findCallers(targetMethod);
        System.out.println("从 " + targetMethod.getFullName() + " 找到调用者数量: " + callers.size());

        for (MethodInfo caller : callers) {
            // 检查调用者是否是Controller方法
            if (controllerApis.containsKey(caller)) {
                List<MethodInfo> newChain = new ArrayList<>(currentChain);
                newChain.add(targetMethod);
                CallChain chain = new CallChain(newChain);
                ApiInfo apiInfo = controllerApis.get(caller);

                Set<ApiInfo> apiSet = impactResults.computeIfAbsent(chain, k -> new HashSet<>());
                apiSet.add(apiInfo);
                System.out.println("找到Controller方法调用链: " + chain.toString() + " -> " + apiInfo.toString());
                continue;
            }

            // 避免循环调用：检查调用者是否已经在当前调用链中
            boolean isInChain = false;
            for (MethodInfo methodInChain : currentChain) {
                if (methodInChain.equals(caller)) {
                    isInChain = true;
                    break;
                }
            }

            if (!isInChain && !caller.equals(targetMethod)) { // 避免自调用
                List<MethodInfo> newChain = new ArrayList<>(currentChain);
                newChain.add(targetMethod);
                System.out.println("追踪调用链: " + caller.getFullName() + " -> " + targetMethod.getFullName());
                traceFromTarget(caller, newChain, depth + 1);
            } else {
                System.out.println("跳过循环调用: " + caller.getFullName());
            }
        }
    }

    /**
     * 查找所有目标方法（匹配类名和方法名）
     */
    private List<MethodInfo> findTargetMethods() {
        List<MethodInfo> targets = new ArrayList<>();
        for (MethodInfo method : allMethods.values()) {
            if (isTargetMethod(method)) {
                targets.add(method);
            }
        }
        return targets;
    }

    /**
     * 判断是否是目标方法
     */
    private boolean isTargetMethod(MethodInfo method) {
        // 支持多种匹配方式
        String methodName = targetMethodName.split("\\.")[1];

        boolean classMatch = method.getClassName().contains(targetClassName);
        boolean methodMatch = method.getMethodName().contains(methodName);
        boolean fullNameMatch = method.getFullName().contains(targetMethodName);

        boolean result = (classMatch && methodMatch) || fullNameMatch;

        if (result) {
            System.out.println("匹配到目标方法: " + method.getFullName() + " (类名匹配:" + classMatch + ", 方法名匹配:" + methodMatch + ", 全名匹配:" + fullNameMatch + ")");
        }

        return result;
    }

    /**
     * 查找调用指定方法的所有方法（调用者）
     */
    private Set<MethodInfo> findCallers(MethodInfo targetMethod) {
        Set<MethodInfo> callers = new HashSet<>();
        System.out.println("查找调用者，目标方法: " + targetMethod.getFullName());
        System.out.println("目标方法详细信息 - 类名: " + targetMethod.getClassName() + ", 方法名: " + targetMethod.getMethodName());

        // 遍历文件的所有方法
        for (Map.Entry<MethodInfo, Set<MethodInfo>> entry : methodCallGraph.entrySet()) {
            MethodInfo caller = entry.getKey();
            Set<MethodInfo> calledMethods = entry.getValue();

            System.out.println("检查调用者: " + caller.getFullName() + " 调用的方法:");
            // 遍历当前方法下调用的方法
            for (MethodInfo calledMethod : calledMethods) {
                System.out.println("  - " + calledMethod.getFullName() + " (类名: " + calledMethod.getClassName() + ", 方法名: " + calledMethod.getMethodName() + ")");

                // 当前方法所调用的方法是要查找的目标方法
                boolean isMatch = calledMethod.equals(targetMethod);

                if (isMatch) {
                    System.out.println("  *** 找到匹配的调用关系! ***");
                    callers.add(caller);
                    break;
                }
            }
        }

        System.out.println("找到调用者数量: " + callers.size());
        return callers;
    }


    /**
     * 获取分析结果
     */
    public Map<CallChain, Set<ApiInfo>> getImpactResults() {
        // 返回副本避免外部修改
        return new LinkedHashMap<>(impactResults);
    }
}
